#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <vector>
#include <unordered_map>
#include <mutex>
#include "../CommonModule/comm.hpp"
#include "../CommonModule/MySQLHelper.hpp"

class User
{
public:
    static const int col_num = 5;

    using Ptr = std::shared_ptr<User>;

    User() {}

    User(const std::string &name, const std::string &id, const std::string &password, 
        const std::string &session, const std::string &head)
        : name(name), id(id), password(password), sessionid(sessionid), head(head)
    {
    }

    ~User() {}

    std::string getUsername()
    {
        return name;
    }

    std::string getUserid()
    {
        return id;
    }

    std::string getPassword()
    {
        return password;
    }

    std::string getSessionid()
    {
        return sessionid;
    }

    std::string getHead()
    {
        return head;
    }

    void setUsername(const std::string &name)
    {
        this->name = name;
    }

    void setUserid(const std::string &id)
    {
        this->id = id;
    }

    void setPassword(const std::string &password)
    {
        this->password = password;
    }

    void setSessionid(const std::string &sessionid)
    {
        this->sessionid = sessionid;
    }

    void setHead(const std::string &head)
    {
        this->head = head;
    }

private:
    std::string name;
    std::string id;
    std::string password;
    std::string sessionid;
    std::string head;
};

class UserPersist
{
public:
    using Ptr = std::shared_ptr<UserPersist>;

    UserPersist(MySQLHelper::Ptr mysql_helper)
        : handler(mysql_helper)
    {
    }

    bool addUser(const std::string &name, const std::string &id, const std::string &password, 
        const std::string &sessionid, const std::string &head)
    {
        const std::string sql = std::string("insert into user(name, id, password, sessionid, head) value(") +
                                "'" + name + "'" + "," +
                                "'" + id + "'" + "," +
                                "'" + password + "'" + "," +
                                "'" + sessionid + "'" + "," +
                                "'" + head + "'" + ");";

        return handler->excuteSQL(sql);
    }

    bool delUser(const std::string &id)
    {
        const std::string sql = std::string("delete from user where id = ") +
                                "'" + id + "'" + ";";

        return handler->excuteSQL(sql);
    }

    User::Ptr getUser(const std::string &id)
    {
        const std::string sql = std::string("select id, name, password, sessionid, head from user where id = ") +
                                "'" + id + "'" + ";";

        User::Ptr ret = nullptr;

        auto func = [&](const std::vector<std::string> &row)
        {
            assert(row.size() == User::col_num);
            ret = std::make_shared<User>();
            ret->setUserid(row[0]);
            ret->setUsername(row[1]);
            ret->setPassword(row[2]);
            ret->setSessionid(row[3]);
            ret->setHead(row[4]);
        };

        handler->excuteSQL(sql, func);

        return ret;
    }

private:
private:
    MySQLHelper::Ptr handler;
};

class UserRedisBuffer
{

public:

    using Ptr  = std::shared_ptr<UserRedisBuffer>;

    bool addUser(const std::string &name, const std::string &id, const std::string &password, 
        const std::string &sessionid, const std::string &head)
    {
        return false;
    }

    bool delUser(const std::string &id)
    {
        return true;
    }

    User::Ptr getUser(const std::string &id)
    {
        return nullptr;
    }
};

class UserBufferDataMagnger
{
public:
    UserBufferDataMagnger(const MySQLHelper::Ptr& mysql_helper)
        : persist(std::make_shared<UserPersist>(mysql_helper))
        , redisbuffer(std::make_shared<UserRedisBuffer>())
    {}
    using Ptr = std::shared_ptr<UserBufferDataMagnger>;

    bool addUser(const std::string &name, const std::string &id, const std::string &password, 
        const std::string &sessionid, const std::string &head)
    {
        if(!persist->addUser(name, id, password, sessionid, head))
        {
            ELOG("添加用户到数据库错误");
            return false;
        }

        redisbuffer->addUser(name, id, password, sessionid, head);
        return true;
    }

    bool delUser(const std::string &id)
    {
        int ret = true;
        if(!redisbuffer->delUser(id))
        {
            ret = false;
            ELOG("从数据库中删除用户出现错误");
        }
        if(!persist->delUser(id))
        {
            ret = false;
            ELOG("从Redis中删除用户缓存出现错误");
        }
        return ret;
    }

    User::Ptr getUser(const std::string &id)
    {
        User::Ptr ret = redisbuffer->getUser(id);
        if(ret)
            return ret;

        ret = persist->getUser(id);
        
        return ret;
    }

private:
    UserPersist::Ptr persist;
    UserRedisBuffer::Ptr redisbuffer;
};

class UserManager
{
public:
    UserManager(const MySQLHelper::Ptr& mysql_helper)
        : userdata_handler(std::make_shared<UserBufferDataMagnger>(mysql_helper))
    {}

    using Ptr = std::shared_ptr<UserManager>;

    // 注册用户
    bool addUser(const std::string &name, const std::string &id, const std::string &password,
            const std::string &session,const std::string &head = defaulthead)
    {
        std::unique_lock<std::mutex> ul(mtx);

        if(users.find(id) != users.end() || userdata_handler->getUser(id) != nullptr)
        {
            DLOG("增添用户失败，用户已经存在");
            return false;
        }

        if(!userdata_handler->addUser(name, id, password, session, head))
        {
            return false;
        }
        return true;
    }
    // 注销用户
    bool delUser(const std::string& id)
    {
        std::unique_lock<std::mutex> ul(mtx);
        if(!userdata_handler->delUser(id))
        {
            return false;
        }

        users.erase(id);
    }

    User::Ptr getUser(const std::string& id)
    {
        std::unique_lock<std::mutex> ul(mtx);
        if(users.find(id) != users.end())
        {
            return users[id];
        }

        User::Ptr ret = userdata_handler->getUser(id);
        return ret;
    }

    Code userLoging(const std::string &id, const std::string &password)
    {
        std::unique_lock<std::mutex> ul(mtx);
        // 用来检测用户是否可以登录
        // 如果users中存在用户id，那么则用户已经登陆，返回对应状态
        if(users.find(id) != users.end())
            return Code::AlreadyLogged;
        // 如果users中不存在 ，且Redis中和MySQL中也 不存在的话，则该用户没有注册
        
        
        // 如果存在的话，获取用户信息，进行密码比对
        User::Ptr user =  userdata_handler->getUser(id);
        if(user == nullptr)
            return Code::UserDoesNotExist;

        if(user->getPassword() != password)
            return Code::PasswordError;

        return Code::Achieved;
    }

    void userLogout(const std::string &id)
    {
        std::unique_lock<std::mutex> ul(mtx);
        DLOG("用户登出");
    }


    ~UserManager()
    {
        users.clear();
    }

private:
    UserBufferDataMagnger::Ptr userdata_handler;
    // 存储的是在线用户
    std::unordered_map<std::string, User::Ptr> users;

    std::mutex mtx;
};